﻿namespace DimensionsHelper.LanguageService.Syntax;

/// <summary>
///     语法解析器得到的语法树
/// </summary>
public class SyntaxTree : SyntaxNode
{
    private readonly List<IDeclaration> _labels;
    private readonly List<Declaration> _signatures;
    private readonly List<InputDataSourceSection> _inputDataSources;
    private readonly Block _root;
    private readonly SourceTextFile _source;
    private DiagnosticStorage? _diagnosticStorage;

    private long _timeStamp;

    internal SyntaxTree(SourceTextFile source, SyntaxTree? entry) :
        base(SyntaxKinds.SyntaxTree, null)
    {
        _source = source;
        _root = NodeFactory.CreateBlock(this);
        _root.SetUri(source.Uri);
        _signatures = [];
        _labels = [];
        _inputDataSources = [];
        Entry = entry ?? this;
        Uri = _source.Uri;
    }


    /// <summary>
    ///     如果根节点不是<see langword="null" />，返回<see langword="true" />
    /// </summary>
    public override bool IsEmpty => _root.IsEmpty;


    /// <summary>
    ///     文件名
    /// </summary>
    public string FileName => _source.FileName;


    /// <summary>
    ///     如果文件是声明文件，返回<see langword="true" />
    /// </summary>
    public bool IsDeclare => _source.Type == SourceFileType.Declare;


    /// <summary>
    ///     如果是DMS文件，返回<see langword="true" />
    /// </summary>
    public bool IsDms => _source.Type == SourceFileType.Dms;


    /// <summary>
    ///     如果文件是元数据文件，返回<see langword="true" />
    /// </summary>
    public bool IsMetadata => _source.Type == SourceFileType.Metadata;


    /// <summary>
    ///     如果语法树中的所有节点都是函数声明，返回<see langword="true" />。
    /// </summary>
    public bool IsPureFunctionLibrary { get; private set; } = true;


    /// <summary>
    ///     语法分析的入口语法树
    /// </summary>
    public SyntaxTree Entry { get; }


    /// <summary>
    ///     如果语法树是入口语法树，返回<see langword="true"/>
    /// </summary>
    public bool IsEntry => Entry == this;

    internal void AddToRoot(Statement statement)
    {
        if (statement.IsOneOfNode(
            SyntaxKinds.ModuleSignature,
            SyntaxKinds.ClassSignature,
            SyntaxKinds.InterfaceSignature,
            SyntaxKinds.EnumSignature,
            SyntaxKinds.FunctionSignature,
            SyntaxKinds.ConstantSignature))
        {
            _signatures.Add((Declaration)statement);
        }
        // InputDataSource
        else if (statement.Is<InputDataSourceSection>(SyntaxKinds.InputDataSourceSection, out var inputDataSource))
        {
            _inputDataSources.Add(inputDataSource);
        }
        else
        {
            if (statement.IsOneOfNode(SyntaxKinds.SectionStatement, SyntaxKinds.Label))
            {
                _labels.Add((IDeclaration)statement);
            }

            IsPureFunctionLibrary = false;
        }

        _root.AddStatement(statement);
    }


    internal void Finish()
    {
        _timeStamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
        _root.SetUri(_source.Uri);
        _root.Update(0, _source.End, _source.GetText());
    }


    /// <summary>
    ///     获取语法树完成时的时间戳。
    /// </summary>
    public long GetTimeStamp()
    {
        return _timeStamp;
    }


    /// <summary>
    ///     获取语法树中的所有节点
    /// </summary>
    public IReadOnlyList<Statement> GetNodes()
    {
        return _root.Statements;
    }


    /// <summary>
    ///     获取语法树中的行标签定义，可能是<see cref="LabelDeclaration" />或
    ///     <see cref="SectionStatement" />。
    /// </summary>
    public IReadOnlyList<IDeclaration> GetLabels()
    {
        return _labels;
    }


    /// <summary>
    ///     获取语法树中的所有本地函数声明节点
    /// </summary>
    public IReadOnlyList<FunctionDeclaration> GetLocalFunctions()
    {
        return _root.Functions;
    }


    /// <summary>
    ///     获取语法树中的所有InputDataSource声明语句块
    /// </summary>
    public IReadOnlyList<InputDataSourceSection> GetInputDataSources()
    {
        return _inputDataSources;
    }


    /// <summary>
    ///     获取语法树中的签名节点
    /// </summary>
    public IReadOnlyList<Declaration> GetSignatures()
    {
        return _signatures;
    }


    /// <summary>
    ///     获取语法树的入口源文件
    /// </summary>
    public SourceTextFile GetEntryFile()
    {
        return Entry._source;
    }


    /// <summary>
    ///     获取语法树通过 include 指令引入的语法树列表
    /// </summary>
    /// <returns></returns>
    public SyntaxTree[] GetIncludeSyntaxTrees()
    {
        return _source.GetIncludeFiles()
                      .Select(s => s.GetSyntaxTrees().FirstOrDefault(e => e.Entry == Entry))
                      .Where(e => e != null)
                      .OfType<SyntaxTree>()
                      .ToArray();
    }


    /// <summary>
    ///     获取语法树中的所有错误
    /// </summary>
    public override IEnumerable<DiagnosticInfo> GetDiagnostics()
    {
        return _diagnosticStorage ??= this.GetDiagnosticStorage();
    }


    /// <summary>
    ///     获取指定语法节点上的错误列表
    /// </summary>
    /// <param name="node">语法节点</param>
    public IEnumerable<DiagnosticInfo> GetDiagnostics(ITextNode node)
    {
        return _diagnosticStorage == null ? [] : _diagnosticStorage.GetDiagnostics(node);
    }


    /// <summary>
    ///     清空保存的错误信息
    /// </summary>
    public void ClearDiagnostics()
    {
        this.GetDiagnosticStorage().Clear();
    }


    private ITextNode? FindNode(int position)
    {
        SyntaxNodeVisitor visitor = SyntaxNodeVisitor.GetPositionVisitorInstance(position);
        _root.AcceptVisitor(visitor);
        return visitor.GetToken() as ITextNode ?? visitor.GetNode();
    }


    /// <summary>
    ///     查找指定文本位置的语法节点
    /// </summary>
    /// <param name="position">文本位置，0开始</param>
    public ITextNode? LookupSyntaxNode(int position)
    {
        return FindNode(position);
    }
}


public static partial class SyntaxExtensions
{
    public static SyntaxTree? GetSyntaxTree(this ITextNode node)
    {
        ITextNode? n = node;

        while (n != null)
        {
            if (n.IsNode(SyntaxKinds.SyntaxTree))
            {
                return (SyntaxTree)n;
            }

            n = n.Parent;
        }

        return null;
    }
}